App.require(["htmlTool", "canvasTool"], function (htmlTool, canvasTool) {
    var app = App.getCurrent();

    app.defineLayer("str-line-chart", {
        render: function (next) {
            this.initProperty();
            this.data.isChange = true;
            this.data.isMouseIn = false;
            this.fetch("./index.html", function (text) {
                next(this.renderHTML(text));
            })
        },
        dispose: function () {
            this.removeAnimation();
            this.data.isChange = true;
            this.data.isMouseIn = false;
        },
        removeOption: function (index) {
            this.data.options.splice(index, 1);
            this.data.isChange = true;
            var property = this.property;
            if (property.animation) this.addPropertyAnimation(property.animation, this);
            else this.addChange();
        },
        appendOption: function (option) {
            this.data.options.push(option);
            this.data.isChange = true;
            var property = this.property;
            if (property.animation) this.addPropertyAnimation(property.animation, this);
            else this.addChange();
        },
        insertBefore: function (index, option) {
            this.data.options.splice(index, 0, option);
            this.data.isChange = true;
            var property = this.property;
            if (property.animation) this.addPropertyAnimation(property.animation, this);
            else this.addChange();
        },
        changeOption: function (index, opt) {
            var option = this.data.options[index];
            if (!option) return;
            this.data.isChange = true;
            this.addChange(opt, option);
        },
        drawSelf: function (ctx, ratio) {
            var dObj = this.getPropertyUse(["width", "height", "opacity", "offset", "distance", "x", "y", "linedashoffset", "linedash", "percent", "shadow", "font", "fill", "stroke"]);
            var property = this.property;
            if (typeof dObj.opacity !== "undefined") ctx.globalAlpha = parseFloat(dObj.opacity);
            if (this.hidden || this.data.options.length == 0) return;
            ctx.translate(dObj.x * ratio, dObj.y * ratio);
            this.transformView(ctx, ratio);
            var keyToCanvas = canvasTool.keyToCanvas;
            for (var key in property) {
                var ctxKey = keyToCanvas[key];
                if (ctxKey) {
                    if (ctxKey == "fillStyle" || ctxKey == "strokeStyle") continue;
                    else ctx[ctxKey] = this[key];
                }
            }

            if (dObj.linedash) this.useLineDash(ctx, dObj.linedash, dObj.linedashoffset, ratio);
            if (dObj.shadow) this.useShadow(ctx, dObj.shadow, ratio);
            ctx.strokeStyle = this.getFillOrStrokeStyle(this.stroke);
            var offsetObj = this.createOffsetObj(dObj.offset, ratio, this._offset);
            var direction = this.direction, distance = dObj.distance * ratio;
            var startObj = this._getStartPos(offsetObj, direction, ratio), type = this.type, percent = dObj.percent;
            var startX = startObj.x, startY = startObj.y, options = this.data.options, lastPoint = null;
            if (dObj.fill) {
                ctx.fillStyle = this.getFillOrStrokeStyle(dObj.fill);
                ctx.beginPath();
                ctx.moveTo(startX, startY);
                for (var i = 0; i < options.length; i++) {
                    var offsetPos = this._getOffsetPos(startX, startY, distance, i, direction);
                    var obj = options[i], h = parseFloat(obj.value) * ratio * percent;
                    var posList = this._createPoints(lastPoint, offsetPos, h, type, direction), pos = posList[posList.length - 1];
                    if (i == 0) ctx.lineTo(pos.x, pos.y);
                    else {
                        if (type === "polyline") {
                            ctx.lineTo(pos.x, pos.y);
                        }
                        else if (type === "curve") {
                            p1 = posList[0], p2 = posList[1];
                            ctx.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, pos.x, pos.y);
                        }
                    }
                    lastPoint = pos;
                }
                ctx.lineTo(offsetPos.x, offsetPos.y);
                ctx.closePath();
                ctx.fill();
            }
            
            ctx.beginPath();
            for (var i = 0; i < options.length; i++) {
                var offsetPos = this._getOffsetPos(startX, startY, distance, i, direction);
                var obj = options[i], h = parseFloat(obj.value) * ratio * percent;
                var posList = this._createPoints(lastPoint, offsetPos, h, type, direction), pos = posList[posList.length - 1];
                if (i == 0) ctx.moveTo(pos.x, pos.y);
                else {
                    if (type === "polyline") {
                        ctx.lineTo(pos.x, pos.y);
                    }
                    else if (type === "curve") {
                        p1 = posList[0], p2 = posList[1];
                        ctx.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, pos.x, pos.y);
                    }
                }
                lastPoint = pos;

                
            }
            ctx.stroke();

            for (var i = 0; i < options.length; i++) {
                var offsetPos = this._getOffsetPos(startX, startY, distance, i, direction);
                var obj = options[i], h = parseFloat(obj.value) * ratio * percent;
                var posList = this._createPoints(lastPoint, offsetPos, h, type, direction), pos = posList[posList.length - 1];
                if (obj.marker || this.marker) {
                    var marker = this.getMarkerByStr(obj.marker || this.marker, this, ratio);
                    if (marker) {
                        ctx.save();
                        ctx.translate(pos.x, pos.y);
                        marker.drawToCanvas(ctx, ratio);
                        ctx.restore();
                    }
                }

                if (this.mark) {
                    ctx.textAlign = obj.align || "center";
                    ctx.textBaseline = obj.baseline || "middle";
                    if (obj.font) ctx.font = this.computeFont(obj.font, ratio);
                    ctx.fillStyle = this.getFillOrStrokeStyle(obj.textfill || "black");
                    var offsetTextPos = this._getOffsetTextPos(ctx, offsetPos, obj, h, direction);
                    ctx.fillText(obj.content, offsetTextPos.x, offsetTextPos.y);
                }
            }
        },
        moveLayerHandler: function (ev, ctx, ratio) {
            if (this.containsBounds(ctx, ev.clientX, ev.clientY, ratio)) {
                if (!this.data.isMouseIn) {
                    this.data.isMouseIn = true;
                    this.dispatchCustomEvent("linechartmouseenter", {
                        component: this,
                        event: ev
                    });
                }
            }
            else {
                if (this.data.isMouseIn) {
                    this.data.isMouseIn = false;
                    this.dispatchCustomEvent("linechartmouseleave", {
                        component: this,
                        event: ev
                    });
                }
            }
        },
        clickLayerHandler: function (ev, ctx, ratio) {
            if (this.containsBounds(ctx, ev.clientX, ev.clientY, ratio)) {
                this.dispatchCustomEvent("linechartclick", {
                    component: this,
                    event: ev,
                });
            }
        },
        createPath: function (ctx, ratio) {
            var offsetObj = this.createOffsetObj(this.offset, ratio, this._offset);
            var direction = this.direction, distance = this.distance * ratio, percent = this.percent;
            var startObj = this._getStartPos(offsetObj, direction, ratio), type = this.type;
            var startX = startObj.x, startY = startObj.y, options = this.data.options, lastPoint = null;
            ctx.translate(this.x * ratio, this.y * ratio);
            ctx.beginPath();
            ctx.moveTo(startX, startY);
            for (var i = 0; i < options.length; i++) {
                var offsetPos = this._getOffsetPos(startX, startY, distance, i, direction);
                var obj = options[i], h = parseFloat(obj.value) * ratio * percent;
                var posList = this._createPoints(lastPoint, offsetPos, h, type, direction), pos = posList[posList.length - 1];
                if (i == 0) ctx.lineTo(pos.x, pos.y);
                else {
                    if (type === "polyline") {
                        ctx.lineTo(pos.x, pos.y);
                    }
                    else if (type === "curve") {
                        p1 = posList[0], p2 = posList[1];
                        ctx.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, pos.x, pos.y);
                    }
                }
                lastPoint = pos;
            }
            ctx.lineTo(offsetPos.x, offsetPos.y);
            ctx.closePath();
        },
        _createPoints: function (lastPoint, pos, h, type, direction) {
            var point = { x: pos.x, y: pos.y };
            switch (direction) {
                case "row": point.y = pos.y - h; break;
                case "row-reverse":
                    point.y = pos.y + h; break;
                case "column":
                    point.x = pos.x + h;
                    break;
                case "column-reverse":
                    point.x = pos.x - h;
                    break;
            }
            if (type == "polyline") {
                return [point];
                
            }
            else if (type === "curve") {
                if (lastPoint) {
                    var pointList = [{x: lastPoint.x, y: lastPoint.y}, {x: point.x, y: point.y}, point];
                    switch (direction) {
                        case "row": 
                        case "row-reverse":
                            pointList[0].x = pointList[1].x = (point.x + lastPoint.x) / 2;
                            break;
                        case "column":
                        case "column-reverse":
                            pointList[0].y = pointList[1].y = (point.y + lastPoint.y) / 2;
                            break;
                    }
                    return pointList;
                }
                return [point];
            }
        },
        _getOffsetPos: function (startX, startY, distance, i, direction) {
            switch (direction) {
                case "row": 
                case "row-reverse": 
                    return {
                        x: startX + i * distance, y: startY
                    };
                case "column": 
                case "column-reverse": return {
                    x: startX, y: startY + i * distance
                }
            }
        },
        _getOffsetTextPos: function (ctx, pos, obj, h, direction) {
            switch (direction) {
                case "row":
                    var ox = obj.offsetx || 0, oy = obj.offsety || -10;
                    return { x: pos.x + ox, y:pos.y - h + oy };
                case "row-reverse":
                    var ox = obj.offsetx || 0, oy = obj.offsety || 10;
                    return { x: pos.x + ox, y:pos.y + h + oy };
                case "column":
                    var ox = obj.offsetx || 10, oy = obj.offsety || 0;
                    return { x: pos.x + h + ox + ctx.measureText(obj.content).width / 2, y: oy + pos.y };
                case "column-reverse":
                    var ox = obj.offsetx || -10, oy = obj.offsety || 0;
                    return { x: pos.x - h + ox - ctx.measureText(obj.content).width / 2, y: oy + pos.y };
            }
        },
        _getStartPos: function (offsetObj, direction, ratio) {
            switch (direction) {
                case "row": return {
                    x:  offsetObj.x,
                    y: this.height * ratio - offsetObj.y
                };
                case "row-reverse": 
                case "column":
                    return {
                        x: offsetObj.x,
                        y: offsetObj.y
                    };
                case "column-reverse":
                    return {
                        x: -offsetObj.x + this.width * ratio,
                        y: offsetObj.y
                    };
            }
        },
        initProperty: function () {
            var property = this.property;
            property.id = property.id || this.uid;

            this.data.attributeStr = this._createPropertyStr(property);
            this.data.options = htmlTool.getOptions(this.slot);
            if (property.animation) this.addPropertyAnimation(property.animation, this);

            this._offset = { x: 0, y: 0 };
        },
        get marker() {
            return this.getValue("marker");
        },
        set marker(value) {
            this.setValue("marker", value);
        },
        get type() {
            return this.getValue("type");
        },
        set type(value) {
            this.setValue("type", value);
        },
        set height(value) {
            this.setValue("height", value);
        },
        get height() {
            return this.getValue("height");
        },
        set width(value) {
            this.setValue("width", value);
        },
        get width() {
            return this.getValue("width");
        },
        get direction() {
            return this.getValue("direction");
        },
        set direction(value) {
            this.setValue("direction", value);
        },
        get distance() {
            return this.getValue("distance");
        },
        set distance(value) {
            this.setValue("distance", value);
        },
        get offset() {
            return this.getValue("offset");
        },
        set offset(value) {
            this.setValue("offset", value);
        },
        get fill() {
            return this.getValue("fill");
        },
        set fill(value) {
            this.setValue("fill", value);
        },
        get stroke() {
            return this.getValue("stroke");
        },
        set stroke(value) {
            this.setValue("stroke", value);
        },
        get linedash() {
            return this.getValue("linedash(");
        },
        set linedash(value) {
            this.setValue("linedash(", value);
        },
        get linedashoffset() {
            return this.getValue("linedashoffset");
        },
        set linedashoffset(value) {
            this.setValue("linedashoffset", value);
        },
        get transform() {
            return this.getValue("transform");
        },
        set transform(value) {
            this.setValue("transform", value);
        },
        get shadow() {
            return this.getValue("shadow");
        },
        set shadow(value) {
            this.setValue("shadow", value);
        },
        get hidden() {
            return this.getValue("hidden");
        },
        set hidden(value) {
            this.setValue("hidden", value);
        },
        get x() {
            return this.getValue("x");
        },
        set x(value) {
            this.setValue("x", value);
        },
        get y() {
            return this.getValue("y");
        },
        set y(value) {
            this.setValue("y", value)
        },
        get mark() {
            return this.getValue("mark");
        },
        set mark(value) {
            this.setValue("mark", value)
        },
        get percent() {
            return this.getValue("percent");
        },
        set percent(value) {
            this.setValue("percent", value)
        },
        get animation() {
            return this.getValue("animation");
        },
        set animation(value) {
            if (this.setValue("animation", value)) {
                if (value) this.addPropertyAnimation(value, this);
            }
        },
        set transition(value) {
            this.setValue("transition", value);
        },
        get transition() {
            return this.getValue("transition");
        },
        set opacity(value) {
            this.setValue("opacity", value);
        },
        get opacity() {
            return this.getValue("opacity");
        }
    });
});